﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DeviceIOControlHandle.cs" company="Matthias Friedrich">
//   Copyright © Matthias Friedrich 2009
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace DeviceManagement
{
	using System;
	using System.Runtime.InteropServices;

	using Microsoft.Win32.SafeHandles;

	[StructLayout(LayoutKind.Sequential)]
	internal struct SECURITY_ATTRIBUTES
	{
		public uint nLength;

		public IntPtr lpSecurityDescriptor;

		public bool bInheritHandle;
	}

	public class DeviceIOControlHandle : SafeHandleZeroOrMinusOneIsInvalid
	{
		private const uint GENERIC_READ = 0x80000000;

		private const uint GENERIC_WRITE = 0x40000000;

		private const uint GENERIC_EXECUTE = 0x20000000;

		private const uint GENERIC_ALL = 0x10000000;

		internal const uint FILE_SHARE_READ = 0x00000001;

		internal const uint FILE_SHARE_WRITE = 0x00000002;

		private const uint FILE_SHARE_DELETE = 0x00000004;

		private const uint CREATE_ALWAYS = 2;

		private const uint CREATE_NEW = 1;

		private const uint OPEN_ALWAYS = 4;

		private const uint OPEN_EXISTING = 3;

		private const uint TRUNCATE_EXISTING = 5;

		private const uint FILE_ATTRIBUTE_READONLY = 0x00000001;

		private const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002;

		private const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004;

		private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;

		private const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020;

		private const uint FILE_ATTRIBUTE_DEVICE = 0x00000040;

		private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

		private const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100;

		private const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200;

		private const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;

		private const uint FILE_ATTRIBUTE_COMPRESSED = 0x00000800;

		private const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000;

		private const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;

		private const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;

		private const uint FILE_ATTRIBUTE_VIRTUAL = 0x00010000;

		private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;

		private const uint FILE_FLAG_OVERLAPPED = 0x40000000;

		private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;

		private const uint FILE_FLAG_RANDOM_ACCESS = 0x10000000;

		private const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;

		private const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;

		private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

		private const uint FILE_FLAG_POSIX_SEMANTICS = 0x01000000;

		private const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;

		private const uint FILE_FLAG_OPEN_NO_RECALL = 0x00100000;

		private const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;

		private readonly string _devicePath;

		public DeviceIOControlHandle(string devicePath, DeviceAccess accessFlags)
			: base(true)
		{
			if (string.IsNullOrEmpty(devicePath))
			{
				throw new ArgumentNullException("devicePath");
			}

			this._devicePath = devicePath;

			this.Open(accessFlags);
		}

		public string DevicePath
		{
			get
			{
				return this._devicePath;
			}
		}

		public bool Send(uint dwIoControlCode)
		{
			uint bytesReturned = 0;

			return this.Send(dwIoControlCode, IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned);
		}

		public bool Send(uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned)
		{
			return DeviceIoControl(this.handle, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, ref lpBytesReturned, IntPtr.Zero);
		}

		protected override bool ReleaseHandle()
		{
			if (this.handle != null)
			{
				CloseHandle(this.handle);
			}

			return true;
		}

		[DllImport("Kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)]
		private static extern bool CloseHandle(IntPtr hObject);

		[DllImport("Kernel32.dll", EntryPoint = "CreateFile", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

		[DllImport("Kernel32.dll", EntryPoint = "CreateFile", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

		[DllImport("Kernel32.dll", EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped);

		private void Open(DeviceAccess accessFlags)
		{
			this.handle = CreateFile(this._devicePath, (uint)accessFlags, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
		}
	}
}